package com.jfinal.ext.config;

import com.jfinal.ext.config.annotation.Config;
import com.jfinal.ext.config.execption.ConfigException;
import com.jfinal.kit.JsonKit;
import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;

/**
 * @author Timor
 * @version 1.0
 * @date 2022/11/21 20:28:47
 */
public class ConfigCenter {
    private static final Map<String, Class<? extends IConfig>> CONFIG = new HashMap<>();
    private final ConcurrentMap<String, IConfig> CACHE = new ConcurrentHashMap<>();
    private final Map<String, Consumer<IConfig>> LISTENER = new HashMap<>();
    private final IConfigProvider provider;

    private boolean finish = false;

    public ConfigCenter(@NotNull IConfigProvider provider) {
        this.provider = provider;
    }

    public static ConfigCenter withFileProvider(String resourcePath) {
        return new ConfigCenter(new FileConfigProvider(name ->
                new File(resourcePath, getConfigFile(name))));
    }

    private static String getConfigFile(String configName) {
        final Class<? extends IConfig> clazz = CONFIG.get(configName);

        if (null == clazz) {
            throw new ConfigException("config not exists: " + configName);
        }


        final Config annotation = clazz.getAnnotation(Config.class);

        if (null != annotation) {
            return annotation.path();
        }
        throw new ConfigException("missing config annotation: " + configName);
    }

    public synchronized void addListener(String name, Consumer<IConfig> consumer) {
        LISTENER.put(name, consumer);
    }

    private void onConfigChanged(String name, IConfig config) {
        final Consumer<IConfig> consumer = LISTENER.get(name);

        if (null != consumer) {
            consumer.accept(config);
        }
    }

    public void finish() {
        finish = true;
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    private <T> T loadConfig(@NotNull String config, @NotNull Class<T> clazz) {
        final String temp = provider.getOrDefault(config, "{}");

        return JsonKit.parse(temp, clazz);
    }


    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


    public void addConfig(@NotNull Class<? extends IConfig> clazz) {
        final Config config = clazz.getAnnotation(Config.class);

        addConfig(config.name(), clazz);
    }

    public void addConfig(@NotNull String config,
                          @NotNull Class<? extends IConfig> clazz) {
        if (finish) {
            return;
        }


        if (clazz == CONFIG.computeIfAbsent(config, key -> clazz)) {
            final IConfig configObject = loadConfig(config, clazz);

            CACHE.put(config, configObject);

            onConfigChanged(config, configObject);
            return;
        }

        throw new ConfigException("config already added!");
    }

    @SuppressWarnings("unchecked")
    public <T extends IConfig> T getConfig(@NotNull String config) {
        return (T) CACHE.get(config);
    }

    public <T extends IConfig> T getConfig(@NotNull Class<T> clazz) {
        final Config config = clazz.getAnnotation(Config.class);

        return getConfig(config.name());
    }

    public boolean saveConfig(@NotNull IConfig value) {
        final Config config = value.getClass().getAnnotation(Config.class);

        return saveConfig(config.name(), value);
    }

    public boolean saveConfig(@NotNull String config, IConfig value) {
        if (null == value) {
            return false;
        }

        // 类型必须相同才ok
        if (!value.getClass().equals(CONFIG.get(config))) {
            return false;
        }


        final boolean result = provider.save(config, JsonKit.toJson(value));

        if (result) {
            CACHE.put(config, value);

            onConfigChanged(config, value);
        }
        return result;
    }

}
